function Canvas(div_id)
{
    BORDER_WIDTH = 3;
    Z_INDEX_BASE = 2;
    
    var self = this;

    this._divId = div_id;  
    
    this._currentRect = null;
    this._startX = -1;
    this._startY = -1;
    this._state = 'nothing';
    this._resizeMode = '';
    this._isRectClicked = false;
    this._idCounter = 0;
    this._canvasX = -1;
    this._canvasY = -1;
    
    this._rects = {};
    
    this._canvas = $('#' + this._divId)[0];
    this._canvas.style.zIndex = Z_INDEX_BASE;
    $(this._canvas).addClass('canvas');

    var r = $(this._canvas).offset();
    this._canvasX = r.left;
    this._canvasY = r.top;
    delete r;

    $(this._canvas).mousedown(function(e) {return self._canvasMouseDown(e)});
    $(this._canvas).mouseup(function(e) {return self._canvasMouseUp(e)});
    $(this._canvas).mousemove(function(e) {return self._canvasMouseMove(e)});
    $(this._canvas).keydown(function(e) {return self._canvasKeyDown(e)});

    this.clearCanvas = function()
    {
        $(this._canvas).find('.rect').remove();
        this._idCounter = 0;
        return false;
    }
    
    this._makeRectCurrent = function(rect)
    {
        this._currentRect = rect;            
        var minIndex = 255;
        var maxIndex = 0;
        for (var i in this._rects)
        {
            var r = this._rects[i];
            if (!r.style.zIndex || r.style.zIndex < Z_INDEX_BASE + 1)
                r.style.zIndex = Z_INDEX_BASE + 1;
            if (r.style.zIndex < minIndex)
                minIndex = r.style.zIndex;                
            if (r.style.zIndex > maxIndex)
                maxIndex = r.style.zIndex;                
        }
        minIndex -= Z_INDEX_BASE + 1;
        for (var i in this._rects)
        {
            this._rects[i].style.zIndex -= minIndex;
            $(this._rects[i]).removeClass('selected');
        }
        if (rect)
        {
            rect.style.zIndex = maxIndex - minIndex + 1;
            $(rect).addClass('selected');
        }
		
		$(this._canvas).trigger('currentRectChanged', [rect]);
    }
    
    this.createRect = function(x, y, width, height, attrs)
    {
        this._idCounter += 1;
        var id = 'rect' + this._idCounter;
        $(this._canvas).append('<div id="' + id + '" class="rect"></div>');
        var rect = $(this._canvas).find('#' + id)[0];
    
        this._startX = x;
        this._startY = y;
        
        rect.style.width = (typeof(width) != 'undefined' ? width : 0) + 'px';
        rect.style.height = (typeof(height) != 'undefined' ? height : 0) + 'px';            
        rect.style.left = this._startX + 'px';
        rect.style.top = this._startY + 'px';
        
        $(rect).append('<div style="float: right;"><a href="#">x</a></div>');
        var a = $(rect).find('div a');
        a.click(function(e) {self.deleteRect(id); return false;});
        
        $(rect).mousedown(function(e) {return self._rectMouseDown(e)});
        $(rect).mouseover(function(e) {return self._rectMouseOver(e)});
        $(rect).mouseup(function(e) {return self._rectMouseUp(e)});
        $(rect).mousemove(function(e) {return self._rectMouseMove(e)});
        $(rect).mouseout(function(e) {return self._rectMouseOut(e)});
        //$(this._currentRect).keydown(function(e) {return self._canvasKeyDown(e)});
        
        this._rects[id] = rect;
		
		if (attrs)
			for (var k in attrs)
				$(rect).attr(k, attrs[k]);
		
		$(this._canvas).trigger('rectCreated', [id]);

        this._makeRectCurrent(rect);
        return id;
    }
    
    this.deleteRect = function(id)
    {
        if (id == this._currentRect.id)
            this._makeRectCurrent(null);
        $(this._rects[id]).remove();
        delete this._rects[id];
		
		$(this._canvas).trigger('rectDeleted', [id]);		
    }
    
    this._canvasMouseDown = function(e)
    {
        this.createRect(e.clientX - this._canvasX, e.clientY - this._canvasY);    
        this._state = 'drawing';        
        return false;
    }
    
    this._canvasMouseUp = function(e)
    {
        if ((this._state == 'drawing') &&
            (e.clientX == this._startX + this._canvasX) && (e.clientY == this._startY + this._canvasY))
        {
            this.deleteRect(this._currentRect.id);
            this._makeRectCurrent(null);
        }
        if (this._currentRect)
        {
            this._rectMouseUp(e);
            //this._makeRectCurrent(null);
        }

        this._state = 'nothing';        
        this._isRectClicked = false;
        
        return false;
    }
           
    this._canvasMouseMove = function(e)
    {
        if (this._state == 'drawing')
        {
            var currentX = e.clientX - this._canvasX;
            var currentY = e.clientY - this._canvasY;
        
            if (currentX > this._startX)
            {
                this._currentRect.style.width = (currentX - this._startX) + "px";
                this._currentRect.style.left = this._startX + "px";                    
            }
            else
            {
                this._currentRect.style.width = (-currentX + this._startX) + "px";
                this._currentRect.style.left = currentX + "px";                    
            }
            if (currentY > this._startY)
            {
                this._currentRect.style.height = (currentY - this._startY) + "px";
                this._currentRect.style.top = this._startY + "px";                    
            }
            else
            {
                this._currentRect.style.height = (-currentY + this._startY) + "px";
                this._currentRect.style.top = currentY + "px";                    
            }
	    $(this._canvas).trigger('rectResized', [this._currentRect]);
        }    
        else
        {
            if (this._isRectClicked && this._currentRect && (['moving', 'resizing'].indexOf(this._state) >= 0))
                this._rectMouseMove(e);
        }
        
        return false;
    }        

//    this._canvasKeyDown = function(e)
//    {
//        if (this._currentRect && e.keyCode && (e.keyCode == 46))
//        {
//            this.deleteRect(this._currentRect.id);
//            return false;
//        }
//        return true;
//    }
    
    this._getRectCursorName = function(rect, border, x, y, is_clicked)
    {
        if (this._state == 'drawing' || !rect || !rect.style)
            return 'default';
        
        if (this._isRectClicked && rect.style)
            return rect.style.cursor || 'default';
        
        var d = ['', ''];
        if (rect.getBoundingClientRect) 
			rect = rect.getBoundingClientRect();
		else if (rect.getRect)
			rect = rect.getRect()
		else
		{
			var off = $(rect).offset(); 
			rect = {
				left: off.left,
				top: off.top,
				right: off.left + rect.clientWidth,
				bottom: off.top + rect.clientHeight
			}
		}

        if ((x >= rect.left) && (x <= rect.left + border))
            d[1] = 'w';
        if ((x >= rect.right - border) && (x <= rect.right))
            d[1] = 'e';
        if ((y >= rect.top) && (y <= rect.top + border))
            d[0] = 'n';
        if ((y >= rect.bottom - border) && (y <= rect.bottom))
            d[0] = 's';
            
        this._resizeMode = d.join('');
        if (this._resizeMode)
            return this._resizeMode + '-resize';
    
        if ((x >= rect.left) && (x <= rect.right) && (y >= rect.top) && (y <= rect.bottom))
            if (is_clicked)
                return 'move';
            else
                return 'pointer';
        
        return 'default'
    }
	
	this._fixRectBounds = function(bounds, mode)
	{
		if (typeof(mode) == 'undefined')
			mode = 'move';
		bounds.left = Math.max(0, bounds.left);
		bounds.top = Math.max(0, bounds.top);
		bounds.width += BORDER_WIDTH;
		bounds.height += BORDER_WIDTH;
		if (mode == 'move')
		{
			bounds.left = Math.min(bounds.left, this._canvas.clientWidth - bounds.width);
			bounds.top = Math.min(bounds.top, this._canvas.clientHeight - bounds.height);
			bounds.left = Math.max(0, bounds.left);
			bounds.top = Math.max(0, bounds.top);			
		}
		bounds.width = Math.min(bounds.width, this._canvas.clientWidth - bounds.left);
		bounds.height = Math.min(bounds.height, this._canvas.clientHeight - bounds.top);
		bounds.width -= BORDER_WIDTH;
		bounds.height -= BORDER_WIDTH;
		bounds.width = Math.max(0, bounds.width);
		bounds.height = Math.max(0, bounds.height);			
		return bounds;
	}
    
    this._rectMouseDown = function(e)
    {
        e.bubbles = false;
        this._makeRectCurrent(e.currentTarget);
        this._currentRect.style.cursor =
            this._getRectCursorName(this._currentRect, BORDER_WIDTH, e.clientX, e.clientY, true);
        this._startX = e.clientX;
        this._startY = e.clientY;
        if (this._resizeMode)
            this._state = 'resizing';
        else
            this._state = 'editing';
    
        this._isRectClicked = true;
        return false;
    }
    
    this._rectMouseUp = function(e)
    {
        this._isRectClicked = false;
        e.currentTarget.style.cursor =
            this._getRectCursorName(e.currentTarget, BORDER_WIDTH, e.clientX, e.clientY, false);
        if (this._state == 'moving')
            this._state = 'editing'
        else
            this._state = 'nothing';
        return false;
    }
    
    this._rectMouseOver = function(e)
    {
        if (this._state == 'drawing')
            return true;
        if (this._state == 'nothing')
            e.currentTarget.style.cursor =
                this._getRectCursorName(e.currentTarget, BORDER_WIDTH, e.clientX, e.clientY, false);
        return false;
    }
    
    this._rectMouseMove = function(e)
    {
        if (this._state == 'drawing')
            return true;
    
        if (this._isRectClicked && (this._state == 'editing'))
            this._state = 'moving';
        if (this._currentRect)    
            this._currentRect.style.cursor =
                this._getRectCursorName(this._currentRect, BORDER_WIDTH, e.clientX, e.clientY, this._isRectClicked);
        
        if (!this._isRectClicked)
            return false;
        if (this._state == 'moving')
        {
            var left = this._currentRect.offsetLeft + e.clientX  - this._startX;
            left = Math.max(left, 0);
            left = Math.min(left, this._canvas.clientWidth - this._currentRect.offsetWidth);
            var top = this._currentRect.offsetTop + e.clientY - this._startY;
            top = Math.max(top, 0);
            top = Math.min(top, this._canvas.clientHeight - this._currentRect.offsetHeight);
            this._startX = e.clientX;
            this._startY = e.clientY;
            this._currentRect.style.left = left + 'px';
            this._currentRect.style.top = top + 'px';
			
			$(this._canvas).trigger('rectMoved', [this._currentRect]);
        }
        if (this._state == 'resizing')
        {
            if (!this._resizeMode)
                return true;
            
            var delta_x = e.clientX  - this._startX;
            var delta_y = e.clientY  - this._startY;
            
            var left = this._currentRect.offsetLeft;
            var top = this._currentRect.offsetTop;
            var width = this._currentRect.clientWidth;
            var height = this._currentRect.clientHeight;
            
            if (/w/.test(this._resizeMode))
            {
                left += delta_x;   
                width -= delta_x;
            }
            if (/n/.test(this._resizeMode))
            {
                top += delta_y;
                height -= delta_y;
            }
            if (/e/.test(this._resizeMode))
                width += delta_x;   
            if (/s/.test(this._resizeMode))
                height += delta_y;   
            
            var b = this._fixRectBounds({left: left, top: top, width: width, height: height});			
			
			this._currentRect.style.width = b.width + 'px';
            this._currentRect.style.height = b.height + 'px';        
            this._currentRect.style.left = b.left + 'px';
            this._currentRect.style.top = b.top + 'px';        
    
            this._startX = e.clientX;
            this._startY = e.clientY;

			$(this._canvas).trigger('rectResized', [this._currentRect]);
        }
        
        return false;
    }
    
    this._rectMouseOut = function(e)
    {
        if (!this._isRectClicked && this._currentRect)
            this._currentRect.style.cursor = 'default';
        return false;
    }
}


